package limfic.persistencia.mysql;

import java.sql.Connection;
//import java.sql.CallableStatement; //Import para usar procedimientos almacenados
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
//import java.util.Date;
import java.util.List;


import limfic.persistencia.IMapeadorFila;
import limfic.persistencia.RepositorioException;
import limfic.persistencia.UtilidadesDB;

/**
 * Clase abstracta que implementa operaciones genericas en la base de datos
 * <p>El parametro E quiere decir que puede recibir cualquier clase o entity y leer
 * sus datos</p>
 * @author OpenCodes
 * @param <E>
 * @see IRepositorioGenerico
 */
public abstract class RepositorioGenerico<E> implements IRepositorioGenerico<E>, IMapeadorFila<E> {

    protected Connection connection;

    public Connection getConnection() {
        return connection;
    }

    public RepositorioGenerico(Connection connection) {
        this.connection = connection;
    }

    /**
     * Agrega a la base de datos los datos de cualquier clase.
     * <p>
     * Utiliza una conexion, un Statement y una String de SQL -sqlAgregar()- que
     * es ejecutada con la base de datos. El sqlAgregar() es cargado por la subclase
     * </p>
     * @param entity
     * @throws OpenCodes.Persistencia.RepositorioException
     */
    @Override
    public void agregar(E entity) throws RepositorioException {


        String sqlAgregar;


        //la subclase implementa el metodo, ya que este es abstracto
        //y para que lo arme el generic le pasa los datos del entity, que puede ser cualquier clase.
        sqlAgregar = sqlAgregar(entity);

        //CallableStatement prcAgregar = null; //Clase de java que utiliza procedimientos almacenados

        PreparedStatement pstAgregar = null;

        try {

            pstAgregar = connection.prepareStatement(sqlAgregar);
            pstAgregar.executeUpdate();

        /*
        prcAgregar = connection.prepareCall(sqlAgregar);
        prcAgregar.execute();
         */

        } catch (SQLException e) {
            throw new RepositorioException("Fallo la insercion del " + entity.getClass().getName(), e);
        } finally {
            UtilidadesDB.close(pstAgregar);
        }


    }

    /**modificar(E entity) se conecta a la base, lee el sqlModificar() cargado por la subclase y despues ajecuta el update*/
    @Override
    public void modificar(E entity) throws RepositorioException {
        String sqlModificar;


        //la subclase implementa el metodo, ya que este es abstracto
        //y para que lo arme el generic le pasa los datos del entity, que puede ser cualquier clase.
        sqlModificar = sqlModificar(entity);

        //CallableStatement prcAgregar = null; //Clase de java que utiliza procedimientos almacenados

        PreparedStatement pstModificar = null;

        try {

            pstModificar = connection.prepareStatement(sqlModificar);
            pstModificar.executeUpdate();

        /*
        prcAgregar = connection.prepareCall(sqlAgregar);
        prcAgregar.execute();
         */

        } catch (SQLException e) {
            throw new RepositorioException("Fallo la modificacion de " + entity.getClass().getName() , e);
        } finally {
            UtilidadesDB.close(pstModificar);
        }


    }

    /**eliminar(E entity) se conecta a la base, lee el sqlEliminar() cargado por la subclase y despues ajecuta el delete */
    @Override
    public void eliminar(E entity) throws RepositorioException {

        String sqlEliminar;


        //la subclase implementa el metodo, ya que este es abstracto
        //y para que lo arme el generic le pasa los datos del entity, que puede ser cualquier clase.
        sqlEliminar = sqlEliminar(entity);

        //CallableStatement prcAgregar = null; //Clase de java que utiliza procedimientos almacenados

        PreparedStatement pstEliminar = null;

        try {

            pstEliminar = connection.prepareStatement(sqlEliminar);
            pstEliminar.executeUpdate();

        /*
        prcAgregar = connection.prepareCall(sqlAgregar);
        prcAgregar.execute();
         */

        } catch (SQLException e) {
            throw new RepositorioException("Fallo la eliminacion del " + entity.getClass().getName(), e);
        } finally {
            UtilidadesDB.close(pstEliminar);
        }

    }

    /**existe se conecta a la base, lee el sqlExiste() cargado por la subclase y despues ajecuta el select */
    @Override
    public boolean existe(String[] parametrosChekeo) throws RepositorioException {
        String sqlExiste;

        sqlExiste = this.sqlExiste(parametrosChekeo);

        Statement stmExiste = null;
      
        ResultSet resultSet = null;
        try {

            //Obtengo los registros utilizando Select Comun
            stmExiste = connection.createStatement();
            stmExiste.execute(sqlExiste);
            resultSet = stmExiste.getResultSet();
            while (resultSet.next()) {
                if (resultSet.getInt(1) > 0) {
                    return true;
                }
            }
            return false;

        } catch (SQLException e) {
            throw new RepositorioException("Fallo el chekeo de duplicacion", e);
        } finally {
            UtilidadesDB.close(resultSet);
            UtilidadesDB.close(stmExiste);
        }
    }

    /**ObtenerUno() se conecta a la base, lee el sqlObtenerUno() cargado por la subclase y devuelve una lista
    con un solo registro */
    @Override
    public List<E> obtenerUno(String id) throws RepositorioException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**ObtenerAlgunos() se conecta a la base, lee el sqlObtenerAlgunos() cargado por la subclase y devuelve una lista
    con algunos registros*/
    @Override
    public List<E> obtenerAlgunos(String[] parametrosBusqueda) throws RepositorioException {
        String sqlObtenerAlgunos;

        sqlObtenerAlgunos = this.sqlObtenerAlgunos(parametrosBusqueda);
        
        Statement stmObtenerAlgunos = null;
        List<E> lista = new ArrayList<E>();


        ResultSet resultSet = null;
        try {
            
            //Obtengo los registros utilizando Select Comun
            stmObtenerAlgunos = connection.createStatement();
            stmObtenerAlgunos.execute(sqlObtenerAlgunos);
            resultSet = stmObtenerAlgunos.getResultSet();
            while (resultSet.next()) {
                E entity = mapearFila(resultSet);
                lista.add(entity);

            }
            return lista;
        } catch (SQLException e) {
            throw new RepositorioException("Fallo la carga de la lista en la busqueda", e);
        } finally {
            UtilidadesDB.close(resultSet);
            UtilidadesDB.close(stmObtenerAlgunos);
        }
    }

    /**ObtenerTodos() se conecta a la base, lee el sqlObtenerTodos() cargado por la subclase y devuelve una lista
    con un todos los registros
     * @return
     * @throws limif.persistencia.RepositorioException
     */
    @Override
    public List<E> obtenerTodos() throws RepositorioException {

        String sqlObtenerTodos;

        sqlObtenerTodos = this.sqlObtenerTodos();
        //CallableStatement prcObtenerTodos = null; //Clase de java que utiliza procedimientos almacenados
        Statement stmObtenerTodos = null;
        List<E> lista = new ArrayList<E>();


        ResultSet resultSet = null;
        try {
            //Obtengo los registros utilizando Procedimientos almacenado
            /*prcObtenerTodos = connection.prepareCall(sqlObtenerTodos);
            prcObtenerTodos.execute();
            resultSet= prcObtenerTodos.getResultSet();*/

            //Obtengo las registros utilizando Select Comun
            stmObtenerTodos = connection.createStatement();
            stmObtenerTodos.execute(sqlObtenerTodos);
            resultSet = stmObtenerTodos.getResultSet();
            while (resultSet.next()) {
                E entity = mapearFila(resultSet);
                lista.add(entity);

            }
            return lista;
        } catch (SQLException e) {
            throw new RepositorioException("Fallo la carga de la lista", e);
        } finally {
            UtilidadesDB.close(resultSet);
            UtilidadesDB.close(stmObtenerTodos);
        }
    }

    /**
     * MapearFila lo arma la subclase.
     * @param resultSet
     * @return
     * @throws java.sql.SQLException
     */
    @Override
    public abstract E mapearFila(ResultSet resultSet) throws SQLException;

    /**
     * sqlExiste lo implementa las subclase
     * lee los datos del servicio propios de cada clase
     */
    protected abstract String sqlExiste(String[] parametrosChekeo);

    /**
     * sqlAgregar lo implementa las subclase
     * lee los datos del servicio propios de cada clase
     */
    protected abstract String sqlAgregar(E entity);

    /**
     * sqlModificar lo implementa las subclase
     * lee los datos del servicio propios de cada clase
     */
    protected abstract String sqlModificar(E entity);

    /**
     * sqlEliminar lo implementa las subclase
     * lee los datos del servicio propios de cada clase
     */
    protected abstract String sqlEliminar(E entity);

    /**
     * sqlObtenerUno lo implementa las subclase
     * lee los datos del servicio propios de cada clase
     */
    protected abstract String sqlObtenerUno(String id);

    /**
     * sqlObtenerAlgunos lo implementa las subclase
     * lee los datos del servicio propios de cada clase
     */
    protected abstract String sqlObtenerAlgunos(String[] parametrosBusqueda);

    /**
     * sqlObtenerTodos lo implementa las subclase
     * lee los datos del servicio propios de cada clase
     */
    protected abstract String sqlObtenerTodos();
}
